﻿using System;
using Dexter.CallContext;
using Dexter.Dependency;
using Dexter.Logger.Contracts;
using Dexter.Scheduler.Quartz.Factory;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;

namespace Dexter.Scheduler.Quartz {
	/// <summary>
	/// The implemenation of <see cref="IScheduler"/> base on <seealso cref="http://quartznet.sourceforge.net/"/>.
	/// </summary>
	public class Scheduler : IScheduler {
		readonly IDexterContainer container;
		readonly ILogger logger;
		readonly global::Quartz.IScheduler scheduler;
		readonly IDexterCall dexterCall;
		readonly ILoggerFactory loggerFactory;

		public Scheduler ( IDexterContainer container, IJobFactory jobFactory , ILogger logger , ILoggerFactory loggerFactory , IDexterCall dexterCall ) {
			this.container = container;
			this.dexterCall = dexterCall;
			this.loggerFactory = loggerFactory;
			this.logger = logger;

			var schedulerFactory = new StdSchedulerFactory ( );
			scheduler = schedulerFactory.GetScheduler ( );
			scheduler.JobFactory = jobFactory;
		}

		#region IScheduler Members

		/// <summary>
		/// Adds the specified <see cref="IJob"/>.
		/// </summary>
		/// <param name="job">The job.</param>
		public void AddJob ( IJob job ) {
			logger.DebugFormat ( "Adding job '{0}'", job.GetType ( ) );
			Tuple <JobDetail , Trigger> t;

			if (job is ICronJob)
				t = QuartzJobDetailFactory.GetJobFromCron ( ( ICronJob )job , dexterCall, loggerFactory);
			else {
				t = QuartzJobDetailFactory.GetJobFromFrequently ( ( IFrequentlyJob )job, dexterCall, loggerFactory );
			}

			scheduler.ScheduleJob ( t.Item1, t.Item2 );

			logger.DebugFormat ( "Job '{0}' added.", job.GetType ( ) );
		}

		/// <summary>
		/// Removes the specified <see cref="IJob"/>.
		/// </summary>
		/// <param name="job">The job.</param>
		public void RemoveJob ( IJob job ) {
			scheduler.DeleteJob ( job.GetType ( ).FullName , job.GroupName );
		}

		/// <summary>
		/// 	Starts the scheduler.
		/// </summary>
		public void StartScheduler ( ) {
			this.StartScheduler ( TimeSpan.Zero );
		}

		/// <summary>
		/// Starts the scheduler.
		/// </summary>
		/// <param name="delay">The delay.</param>
		public void StartScheduler ( TimeSpan delay ) {
			var jobs = container.ResolveAll<IJob> ( );

			foreach (var job in jobs) {
				this.AddJob ( job );
			}

			logger.Debug ( "Starting scheduler" );

			scheduler.StartDelayed ( delay );

			logger.Debug ( "Scheduler started" );
		}

		/// <summary>
		/// 	Stops the scheduler.
		/// </summary>
		public void StopScheduler ( ) {
			logger.Debug ( "Stopping scheduler" );
			scheduler.Standby (  );
			logger.Debug ( "Scheduler stopped" );
		}

		/// <summary>
		/// Determines whether this scheduler is started.
		/// </summary>
		/// <returns>
		/// 	<c>true</c> if this scheduler is started; otherwise, <c>false</c>.
		/// </returns>
		public bool IsStarted ( ) {
			logger.Debug ( "Retrieving scheduler status" );
			var isStarted = scheduler.IsStarted;
			logger.DebugFormat ( "Scheduler status retrieved. IsStarted = {0}" , isStarted );
			return isStarted;
		}

		#endregion
	}
}